package polimi.disttest;

import java.net.ConnectException;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import polimi.reds.broker.overlay.AlreadyNeighborException;
import polimi.reds.broker.overlay.NotRunningException;
import polimi.reds.optimizer.OptimizerStrategyOptions;

public class LocalManager {
	
	public static final String OPTION_SIMULID = "sid";
	public static final String OPTION_INITIAL_NEIGHBORS="initial_neighbors";
	public static final String OPTION_STARTUP_NEIGHBORHOOD_TIMESTAMP="startup_neighborhood_ts";
	public static final String OPTION_STARTUP_TRAFFIC_TIMESTAMP="startup_traffic_ts";
	public static final String OPTION_NUM_COMPONENTS="num_components";
	public static final String OPTION_SEED="seed";
	public static final String OPTION_MESSAGE_CLASSES="message_classes";
	public static final String OPTION_CLASSES_PER_SUBSCRIPTION="classes_per_subscription";
	public static final String OPTION_DURATION="duration";
	public static final String OPTION_NUM_COMPONENT_SUBSCRIPTIONS="num_component_subscriptions";
	public static final String OPTION_DELAY_MESSAGES_MEAN="delay_messages_avg";
	public static final String OPTION_DELAY_MESSAGES_STDEV="delay_messages_stdev";
	public static final String OPTION_IP="ip";
	public static final String OPTION_PORT="port";
	public static final String OPTION_ALGORITHM="algorithm";
	public static final String OPTION_ALGORITHM_PARAMETERS="algorithm_parameters";
	public static final String OPTION_MESSAGES_CLASSES_DISTRIBUTION="publications_distribution";
	public static final String OPTION_SUBSCRIPTIONS_CLASSES_DISTRIBUTION="subscriptions_distribution";
	
	public static void main(String args[]) {
		OptimizerStrategyOptions o = new OptimizerStrategyOptions();
		o.addOption(OPTION_SIMULID, "simulation id", args[27]);
		o.addOption(OPTION_INITIAL_NEIGHBORS, "urls of initial neighbors separed with commas", args[35]);
		o.addOption(OPTION_STARTUP_NEIGHBORHOOD_TIMESTAMP, "startup neighborhood timestamp (s)", args[23]);
		o.addOption(OPTION_STARTUP_TRAFFIC_TIMESTAMP, "startup traffic timestamp (s)", args[25]);
		o.addOption(OPTION_NUM_COMPONENTS, "number of local components", args[19]);
		o.addOption(OPTION_SEED, "random seed", args[21]);
		o.addOption(OPTION_MESSAGE_CLASSES, "number of message classes", args[13]);
		o.addOption(OPTION_CLASSES_PER_SUBSCRIPTION, "message classes per subscription", args[5]);
		o.addOption(OPTION_DURATION, "duration of a simulation (seconds)", args[11]);
		o.addOption(OPTION_NUM_COMPONENT_SUBSCRIPTIONS, "number of subscriptions per component", args[17]);
		o.addOption(OPTION_DELAY_MESSAGES_MEAN, "mean of message delay (ms)", args[7]);
		o.addOption(OPTION_DELAY_MESSAGES_STDEV, "stdev of message delay (ms)", args[9]);
		o.addOption(OPTION_IP, "IP of the local broker", args[33]);
		o.addOption(OPTION_PORT, "port of the local broker", args[31]);
		o.addOption(OPTION_ALGORITHM, "overlay optimization algorithm", args[1]);
		o.addOption(OPTION_ALGORITHM_PARAMETERS, "optimization algorithm parameters", args[3]);
		o.addOption(OPTION_MESSAGES_CLASSES_DISTRIBUTION, "messages classes distribution", args[15]);
		o.addOption(OPTION_SUBSCRIPTIONS_CLASSES_DISTRIBUTION, "subscription classes distribution", args[29]);
		
		// Parse the command line options
		try {
			o.parse(args);
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			System.out.println("For usage information use -help");
			System.exit(0);
		}
		
		long neighborhoodTS=((long) o.getIntegerOption(OPTION_STARTUP_NEIGHBORHOOD_TIMESTAMP))*1000L;
		
		if (neighborhoodTS<System.currentTimeMillis()+3000) {
			long newValue = System.currentTimeMillis()+3000;
			System.out.println("Warning: " + OPTION_STARTUP_NEIGHBORHOOD_TIMESTAMP + " has an invalid value (" + neighborhoodTS + "). Assuming: " + newValue);
			neighborhoodTS=newValue;
		}
		 
		long trafficTS=((long) o.getIntegerOption(OPTION_STARTUP_TRAFFIC_TIMESTAMP))*1000L;
		if (trafficTS<neighborhoodTS+3000) {
			long newValue = neighborhoodTS+3000;
			System.out.println("Warning: " + OPTION_STARTUP_TRAFFIC_TIMESTAMP + " has an invalid value (" + neighborhoodTS + "). Assuming: " + newValue);
			trafficTS=newValue;
		}
		
		Logger.getLogger("polimi.reds.broker.overlay").setLevel(Level.WARNING);
		Logger.getLogger("polimi.reds.optimizer").setLevel(Level.WARNING);
		
		System.out.println();
		
		
		System.out.println("Creating overlay...");
		Broker broker = new Broker(o);
		broker.createOverlay();
		
		System.out.println("Waiting TS=" + neighborhoodTS + " ("+ (neighborhoodTS-System.currentTimeMillis()) +"ms) before connecting neighbors...");
		
		while (System.currentTimeMillis()<neighborhoodTS) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {}
		}
		
		System.out.println("Connecting to Neighbors...");
		
		try {
			broker.connectToNeighbors();
		} catch (AlreadyNeighborException e) {
		} catch (ConnectException e) {
			System.err.println("Unable to connect broker (" + e.getMessage() + ")");
		} catch (MalformedURLException e) {
			System.err.println("Unable to connect broker (" + e.getMessage() + ")");
		} catch (NotRunningException e) {
			System.err.println("Unable to connect broker (" + e.getMessage() + ")");
		}
		
		System.out.println("Neighbors connected... Waiting TS=" + trafficTS + " (" + (trafficTS-System.currentTimeMillis()) + "ms) before starting traffic generating components...");
		
		while (System.currentTimeMillis()<trafficTS) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {}
		}
		
		System.out.println("Starting components...");
		List<Component> components = new ArrayList<Component>();
		for (int i=0; i<o.getIntegerOption(OPTION_NUM_COMPONENTS); i++) {
			Component c = new Component(o);
			c.start();
			components.add(c);
		}
		
		for (int i=0; i<=o.getIntegerOption(OPTION_DURATION); i++) {
			Map<String,Object> stats = broker.getStats();
			stats.put("time", i);
			ResultsAnalyzer results = new ResultsAnalyzer(stats);
			results.writeResultsToConsole(null);
			results.writeResultsToFile(null,o.getOption(OPTION_IP) + "." + o.getOption(OPTION_PORT) + ".txt");
			try {
				Thread.sleep(1000);
			} catch (Exception e) {}
		}
		
		System.out.println("Simulation completed!");
		
		//for (Component c:components) {
		//	c.startTermination();
		//	try {
		//		c.join();
		//	} catch (InterruptedException e) {
		//	}
		//}
		
		//broker.destroyOverlay();
		System.exit(0);
	}

}
